package com.zijing2333.adaptivescheduling.SwitcherManager;

import android.app.AlertDialog;
import android.app.Service;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Binder;
import android.os.CountDownTimer;
import android.os.IBinder;
import android.util.Log;

import com.zijing2333.adaptivescheduling.ChannelManager;
import com.zijing2333.adaptivescheduling.SwitcherManager.client.ClientCallback;
import com.zijing2333.adaptivescheduling.SwitcherManager.server.ServerCallback;
import com.zijing2333.adaptivescheduling.SwitcherManager.utils.NetworkUtil;
import com.zijing2333.adaptivescheduling.SwitcherManager.utils.ThreadUtil;
import com.zijing2333.adaptivescheduling.criticalNodeJudgment.JudgeKeyNode;
import com.zijing2333.adaptivescheduling.switchCondition.isSwitch;
import com.zijing2333.adaptivescheduling.switchCondition.util.BatteryUtil;
import com.zijing2333.adaptivescheduling.switchCondition.util.MemoryUtil;

import java.util.HashSet;

public class SwitcherService extends Service implements ServerCallback, ClientCallback {
    private static final String TAG = "fun_SwitcherService";
    private Switcher mSwitcher;

    private static SwitcherService instance; //主要是让SwitcherInteractionWithDeployer类能获取到SwitcherService，并且调用onNotificationReceived方法存储任务到切换器

    public static SwitcherService getInstance(){
        return instance;
    }

    public Switcher getmSwitcher() {
        return mSwitcher;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        instance = this;
        mSwitcher = new Switcher(this); //创建Switcher类，进而创建了TaskExecuteManager以及TaskRunnerConnectionManager
        startListeningSwitchConditionThread();//开启切换监听的线程
        startListeningJsTaskFinishedThread(); //开启任务结束的监听线程，主要完成关闭socket以及移除map记录
        startSocketServer();
    }


    //开一个新的线程，监控当前设备上是否有执行完成的任务，若有，则进行JS运行map的移除以及运行时环境JS任务的移除
    private void startListeningJsTaskFinishedThread() { //这里是对正常结束的任务进行监听，如果是切换完成后任务的销毁是在
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true){
                    //这里可能是耗时操作，不知道要不要新建一个线程--------------NEW
                    TaskInfoManager.getInstance().updateTaskStates(mSwitcher);
                    HashSet<String> runCompletedSet = TaskInfoManager.getInstance().getRunCompletedTaskSet();
                    if (runCompletedSet.isEmpty()){
                        try {
                            Thread.sleep(1000 * 180); //暂停个3分钟继续检测当前设备上是否有历史任务
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                        continue;
                    }else {
                        for (String taskId : runCompletedSet){ //这里在关闭socket之前，若是检测到是关键节点，还要通知进行副本的清除
                            //这里要不要考虑关闭socket呢？如何知道该任务是否有socket？
                            int SocketEstablishType = TaskInfoManager.getInstance().getSocketEstablishType(taskId);
                            boolean isKeyNode = TaskInfoManager.getInstance().getJsMap().get(taskId).isKeyNode();
                            if (SocketEstablishType== 0){ //说明该对该任务没有建立socket
                                continue;
                            }else if (SocketEstablishType == 1){ //进行客户端socket的关闭
                                ChannelManager.getInstance().stopClient(taskId);
                            }else { //进行服务端socket的关闭
                                //在关闭前需要通知客户端B设备，进行副本的清除工作
                                if (isKeyNode){
                                    ChannelManager.getInstance().sendMesToClient(taskId,"2",""); //通知B设备进行副本的清除工作
                                }
                                try {
                                    Thread.sleep(1000);
                                }catch (InterruptedException e) {
                                    throw new RuntimeException(e);
                                }
                                ChannelManager.getInstance().removeServerConnectClientThreadFromMap(taskId); //关闭线程同时移出map集合
                            }
                            //通知运行时将任务信息删除 mSwitch调用方法 思考
                            mSwitcher.stopJSExecution(taskId);
                            //将jsMap中的信息移除
                            TaskInfoManager.getInstance().removeJsMap(taskId);
                        }
                    }
                }
            }
        }).start();
    }

    //开启切换监听的线程方法
    private void startListeningSwitchConditionThread() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true){
                    //这里可能是耗时操作，不知道要不要新建一个线程--------------NEW
                    TaskInfoManager.getInstance().updateTaskStates(mSwitcher);
                    HashSet<String> runningSet = TaskInfoManager.getInstance().getRunningTaskSet();
                    HashSet<String> waitingCopySet = TaskInfoManager.getInstance().getCopyTaskSet();
                    if(runningSet.isEmpty() && waitingCopySet.isEmpty()){
                        try {
                            Thread.sleep(1000 * 60); //暂停个1分钟继续检测当前设备上是否执行有任务
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                        continue;
                    }
                    int battery = BatteryUtil.getBatteryLevel(SwitcherService.this);//获取电量的值
                    boolean isCharge = BatteryUtil.isCharging(SwitcherService.this); //是否在充电
                    long availableMemory = MemoryUtil.getAvailableMemory(SwitcherService.this); //获取可用内存大小
                    long memoryThreshold = MemoryUtil.getMemoryThreshold(SwitcherService.this); //获取清理后台进程的内存阙值
                    Log.d(TAG, "当前电量为: " + battery +"   " +"当前是否在充电:"+isCharge+"   " +"当前可用内存:"+availableMemory+"   " +"当前内存阙值为:"+memoryThreshold);
                    Log.d(TAG, "本机IP："+NetworkUtil.getIP(SwitcherService.this));
                    isSwitch i = new isSwitch();
                    boolean isKeyNode; //是否是关键节点
                    //对设备上正在运行的所有JS服务进行遍历，如果满足JS服务满足切换要求再进行切换
                    for(String taskId : runningSet){
                        isKeyNode = TaskInfoManager.getInstance().getJsMap().get(taskId).isKeyNode();
                        if (isKeyNode && TaskInfoManager.getInstance().getJsMap().get(taskId).isDeployed()){ //是关节任务，且对端设备上副本已经部署完成了
                            if ((i.isLowBattery(battery) && !isCharge) || i.isLowMemory(availableMemory, memoryThreshold) ) { //这里只是对电量进行了判断，下面还要加上其他的条件
                                //进入该任务的流转模块  现将数据传到主线程当中，在主线程当中调用流传的方法 这样这个子线程可以继续判断还有哪些JS服务可以流转了
                                ThreadUtil.RunInMainThread(new ThreadUtil.ThreadPram() {
                                    @Override
                                    public void Function() {
                                        //保存本设备上的任务状态
                                        String data = mSwitcher.getCurrentExecutionState(taskId);
                                        //发送任务的状态：从soceket客户端发到sockt服务端  其立中会taskId找到已经创建的客户端Socket，即找到已建的socket链路
                                        ChannelManager.getInstance().sendMesToClient(taskId,"1",data);
                                         //迁移完成后从map中移除当前任务----思考还需不需要对端发送一个迁移完成的通知
                                        TaskInfoManager.getInstance().removeJsMap(taskId);
                                        //这里要进行关闭任务的操作----最好让用户点击来判断是否关闭
                                        mSwitcher.stopJSExecution(taskId);
                                        TaskInfoManager.getInstance().removeJsMap(taskId);
                                        //同时关闭任务的socket服务端
                                        ChannelManager.getInstance().removeServerConnectClientThreadFromMap(taskId);
                                    }
                                });
                            }
                        } else { //普通节点  则直接进行切换条件的判断即可
                            if ((i.isLowBattery(battery) && !isCharge) || i.isLowMemory(availableMemory, memoryThreshold)) { //这里只是对电量进行了判断，下面还要加上其他的条件
                                AlertDialog.Builder builder = new AlertDialog.Builder(SwitcherService.this);
                                builder.setTitle("确认操作");
                                builder.setMessage("您确定要执行JS任务流转操作吗？");
                                builder.setPositiveButton("确定", new DialogInterface.OnClickListener() {
                                    @Override
                                    public void onClick(DialogInterface dialog, int which) {
                                        //进入该任务的流转模块  现将数据传到主线程当中，在主线程当中调用流传的方法 这样这个子线程可以继续判断还有哪些JS服务可以流转了
                                        ThreadUtil.RunInMainThread(new ThreadUtil.ThreadPram() {
                                            @Override
                                            public void Function() {
                                                autoStartMigration(taskId); //这里是指建立SOcket连接
                                                //接下来的发送数据的操作，是在服务端A设备收到客户端B设备建立连接请求后，Socket链路完成，详细见ServerConnectClientThread
                                            }
                                        });
                                    }
                                });
                                builder.setNegativeButton("取消", null);
                                builder.show();
                            }
                        }
                    }
                    //这里还需要对本设备上的副本任务进行遍历，看是否满足切换条件的，若满足，需需要切换NEW
                    String sourceIP;
                    for (String taskId :waitingCopySet){ //遍历设备上处于WAITING状态的副本任务，WAITING是指任务还未启动，但已经交给运行时app进行预部署了
                        if ((i.isLowBattery(battery) && !isCharge) || i.isLowMemory(availableMemory, memoryThreshold)) { //这里只是对电量进行了判断，下面还要加上其他的条件
                            //则要通知调度器选择设备，并且把副本迁移过去 这个sourceIP记录的是源关键任务的设备IP地址 很关键
                            sourceIP = TaskInfoManager.getInstance().getCopyMap().get(taskId).getSourceIP();
                            //想调度器发送调度请求
                            String deviceIP = mSwitcher.getDeviceIP2(taskId, sourceIP);
                            //消除本设备上的副本信息
                            TaskInfoManager.getInstance().removeCopyMap(taskId);
                            //关闭socket客户端
                            ChannelManager.getInstance().stopClient(taskId);
                            //同时还要消除运行时里面存的副本代码，调用mSwitch
                            mSwitcher.deleteReplicaCode(taskId);
                        }
                    }
                    try {
                        Thread.sleep(1000 * 120); //暂停个2分钟继续检测
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        }).start();

    }

    private void autoStartMigration(String JsServiceId){
        //这里面的话开启子线程进行流转，因为这里面是耗时操作------获取JS保存的数据、并发送给对方
        String sourceIP = NetworkUtil.getIP(this);
        ThreadUtil.RunInCachedThread(new ThreadUtil.ThreadPram() {
            @Override
            public void Function() {
                //首先是通知调度器选取设备----HTTP
                String ip = mSwitcher.getDeviceIP2(JsServiceId,sourceIP);
               //这里的ip地址应该没什么用
            }
        });
    }

    public class MyBinder extends Binder{
        //getService方法是MyBinder类中的方法，调用的话就可以得到SwitcherService这个Service对象，然后就可以调用Service里面的方法了
        public SwitcherService getService(){
            return SwitcherService.this;
        }
    }
    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        return new MyBinder();
    }

    //收到部署器发来的通知后，切换器会先存储一下任务，然后通知JS管理模块启动JS任务
    //----------注意得加一个一个state参数，state表示的是如果是0，表示的是正常服务工作流任务部署，如果是1，则表示的是关键节点副本部署完成以及非关键建立
    public void onNotificationReceived(SwitchTaskInfo switchTaskInfo, int state, String sourceIP){ //收到部署器发来的通知

        boolean isKeyNode = switchTaskInfo.isKeyNode(); //true表示是关键节点、false反之不是
        Log.d(TAG, "该任务是否为关键任务："+isKeyNode);
        boolean isSuccess = false; //服务是否启动完成
        if (state == 0 && !isKeyNode){ //普通工作流任务，直接启动就行了----针对A设备
            isSuccess = mSwitcher.dispatchJsTask(0,switchTaskInfo.getTaskId(),switchTaskInfo); //type=0表示启动普通任务
            if (isSuccess){
                TaskInfoManager.getInstance().addTask(switchTaskInfo); //存储任务信息同时设置任务状态为RUNNING，
            }else {
                //服务开启失败
                Log.d(TAG, "JS任务未在本设备上成功启动1");
            }
        }else if (state == 0 && isKeyNode){ //普通工作流任务，但是该任务是关键任务，启动该任务的同时还需要部署副本----针对A设备
            Log.d(TAG, "state=0 ，是关键任务-----进行任务开启--------------0");
            isSuccess = mSwitcher.dispatchJsTask(0,switchTaskInfo.getTaskId(),switchTaskInfo); //type=0表示启动普通任务
            Log.d(TAG, "state=0 ，是关键任务-----进行任务开启--------------1");
            if (isSuccess){
                TaskInfoManager.getInstance().addTask(switchTaskInfo); //存储任务信息同时设置任务状态为RUNNING
                //向调度器发送部署副本的通知
                //-----------为了测试，暂时隐藏
        //                String deviceIP = mSwitcher.getDeviceIP2(switchTaskInfo.getTaskId(), NetworkUtil.getIP(this));//向调度器发送本机的IP地址
            }else {
                //服务开启失败
                Log.d(TAG, "JS任务未在本设备上成功启动2");
            }
        }else if (state == 1 && !isKeyNode){ //是从A设备上迁移过来的任务，需要先启动任务，然后马上建立socket连接，并且接收服务状态数据----针对B设备
            isSuccess = mSwitcher.dispatchJsTask(0,switchTaskInfo.getTaskId(),switchTaskInfo); //type=0表示启动普通任务
            Log.d(TAG, "切换服务进行部署============================");
            if (isSuccess){
                TaskInfoManager.getInstance().addTask(switchTaskInfo); //往任务map中添加记录同时设置任务状态为RUNNING
                ChannelManager.getInstance().startClient(sourceIP,this,switchTaskInfo.getTaskId()); //启动客户端
                try {
                    Thread.sleep(1000 * 1); //暂停个1s钟
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                ChannelManager.getInstance().senMesToServer(switchTaskInfo.getTaskId(),"3",NetworkUtil.getIP(this)); //发送通知告知A设备：B设备上的任务已启动好了,同时发送B设备IP地址，帮助A设备上对socket建立联系
                TaskInfoManager.getInstance().setSocketEstablishType(switchTaskInfo.getTaskId(),1); //B设备上该任务的socket已建立，标记之后是为了之后删除该socket能方便一些
            }else {
                //服务开启失败
                Log.d(TAG, "JS任务未在本设备上成功启动3");
            }
        }else if (state == 1 && isKeyNode){ //说明该任务是副本任务，----针对B设备
            Log.d(TAG, "state=1 ，是副本任务-----进行任务开启--------------0");
            isSuccess = mSwitcher.dispatchJsTask(1,switchTaskInfo.getTaskId(),switchTaskInfo);//type=1表示先下载副本代码
            if (isSuccess){
                TaskInfoManager.getInstance().addCopy(switchTaskInfo); //往副本map中添加记录
                TaskInfoManager.getInstance().setCopySourceIp(switchTaskInfo.getTaskId(),sourceIP);//对副本设置源IP地址，如果副本还需要迁移的话用得到
                ChannelManager.getInstance().startClient(sourceIP,this,switchTaskInfo.getTaskId());//启动socket客户端
                try {
                    Thread.sleep(1000 * 1); //暂停个1s钟
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                ChannelManager.getInstance().senMesToServer(switchTaskInfo.getTaskId(),"3",NetworkUtil.getIP(this)); //发送通知告知A设备：B设备上的副本代码已经备份，并返回B设备的IP，让A设备知道IP与任务id对应(socket)
//                TaskInfoManager.getInstance().setSocketEstablishType(switchTaskInfo.getTaskId(),1); //B设备上该任务的socket已建立，标记之后是为了之后删除该socket能方便一些
            }else {
                //服务开启失败
                Log.d(TAG, "副本任务未在本设备上成功启动4");
            }

        }
    }

    //开启服务端Socket
    public void startSocketServer(){
        ChannelManager.getInstance().startServer(this);
    }
    //开启客户端Socket

    public void startSocketClient(String ip,String ServiceId){
        ChannelManager.getInstance().startClient(ip, this,ServiceId);
    }



    @Override
    public void receiveServerMsg(int type,String taskId,String msg,boolean isCopy) {
        if (!isCopy && type==1){ //不是副本任务的话直接运行就好了
            //这里是恢复服务数据
            ThreadUtil.RunInMainThread(new ThreadUtil.ThreadPram() {
                @Override
                public void Function() {
                    Log.d(TAG, "设备B收到设备A发来的任务状态数据："+msg);
                    mSwitcher.restoreServiceDataState(taskId,msg);
                    Log.d(TAG, "设备B上已完成状态恢复1 ");
                    try {
                        Thread.sleep(2000);
                    }catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
//                    ChannelManager.getInstance().stopClient(taskId); //关闭Socket客户端
                }
            });
        }else if(isCopy && type==1) { //是副本任务，需要先开启任务，然后再恢复任务数据
            SwitchTaskInfo switchTaskInfo = TaskInfoManager.getInstance().getTaskInfoInCopy(taskId);
            boolean isSuccess = mSwitcher.dispatchJsTask(2,taskId,switchTaskInfo);//type=2表示启动副本任务（代码已经下载完成了）
            Log.d(TAG, "副本任务开启成功");
            if (isSuccess){//任务启动成功，那么接下来就是进行恢复数据了
                //这里是恢复服务数据
                TaskInfoManager.getInstance().addTask(switchTaskInfo); //该副本任务就成为了设备上正在运行的JS任务
                ThreadUtil.RunInMainThread(new ThreadUtil.ThreadPram() {
                    @Override
                    public void Function() {
                        new CountDownTimer(1500,500){
                            @Override
                            public void onTick(long l) {
                                Log.d(TAG, "onTick: ----倒计时2"+l / 1000);
                            }
                            @Override
                            public void onFinish() {
                                mSwitcher.restoreServiceDataState(taskId,msg);
                                Log.d(TAG, "设备B上已完成状态恢复2");
                                ChannelManager.getInstance().stopClient(taskId); //关闭Socket客户端，同时从clientMap中移除
                            }
                        }.start();
                    }
                });
            }
        }else if (type ==2){ //将运行时环境中的副本代码删除
             mSwitcher.deleteReplicaCode(taskId);
        }
    }

    @Override
    public void receiveClientMsg(String JsServiceId,String clientIp) {
        Log.d(TAG, "A设备接收到B设备发来的任务id与IP地址");
        ChannelManager.getInstance().exchangeIPwithTaskId(clientIp,JsServiceId); //这样就建立了任务id与serverConnectClientThread的一一对应了
        TaskInfoManager.getInstance().setSocketEstablishType(JsServiceId,2); //A 设备上该任务的socket已建立，标记之后是为了之后删除该socket能方便一些

        boolean isKey = TaskInfoManager.getInstance().getJsMap().get(JsServiceId).isKeyNode();
        Log.d(TAG, "是否是keyNode："+isKey);
        if (!isKey){ //普通节点
            //还需要传送数据过去,即把数据从A设备传到B设备上去
            //这里是收到服务端的部署成功的通知，客户端这边就要获取数据并发送数据给服务端了
            ThreadUtil.RunInMainThread(new ThreadUtil.ThreadPram() {
                @Override
                public void Function() {
                    //保存本设备上的任务状态
                    String data = mSwitcher.getCurrentExecutionState(JsServiceId);
                    //发送任务的状态：从socket客户端发到socket服务端  其立中会taskId找到已经创建的客户端Socket，即找到已建的socket链路
                    Log.d(TAG, "A设备发送过去的数据为："+data);
                    ChannelManager.getInstance().sendMesToClient(JsServiceId,"1",data);
                    //思考----加一个是否关闭本设备上的该服务的确认按钮  //这里要进行关闭任务的操作----最好让用户点击来判断是否关闭
//                mSwitcher.stopJSExecution(ServiceId); //这里还有待思考（因为不知道对端设备上服务是否部署完成了，完成了再考虑关闭服务）NEW
//                TaskInfoManager.getInstance().removeJsMap(ServiceId);
//                ChannelManager.getInstance().removeServerConnectClientThreadFromMap(ServiceId);
                    Log.d(TAG, "已发送任务状态数据到B设备 ");
                }
            });
        }else{ //对A设备而言，B设备上的该任务副本已经预部署好了
            Log.d(TAG, "关键节点JS任务已经传到了B设备上了，且已经保存好代码了");
            TaskInfoManager.getInstance().getJsMap().get(JsServiceId).setDeployed(true);//添加集合：对端设备上该副本任务已部署完成
        }
    }

    @Override
    public void otherMsg(String msg) {

    }
}